package io.persimmon.queue.core;

import io.persimmon.core.exception.PersimmonExceptionFactory;
import io.persimmon.queue.bo.Context;
import io.persimmon.queue.bo.Listener;
import io.persimmon.queue.bo.Message;
import io.persimmon.springboot.aop.MethodAopInfo;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public abstract class AbsQueue<A extends Annotation, T, C extends Context, M extends Message<T>> implements Queue<T, C, M>, BeanPostProcessor {

    public abstract Class<A> annotationClass();

    public abstract M createMessage(A annotation, Object[] args);

    public abstract C createContext(A annotation);

    public abstract void executeService();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        this.registerMethod(bean);
        return bean;
    }

    protected void registerMethod(Object bean) {
        Method[] methods = MethodUtils.getMethodsWithAnnotation(bean.getClass(), annotationClass());
        if (ArrayUtils.isEmpty(methods)) {
            return;
        }
        for (Method method : methods) {
            this.checkMethod(method);
            A annotation = method.getAnnotation(annotationClass());
            Listener<T, M> listener = this.createListener(annotation, bean, method);
            C context = this.createContext(annotation);
            this.registerListener(listener, context);
        }
    }

    protected void checkMethod(Method method) {
        Class<?> returnType = method.getReturnType();
        if (!void.class.equals(returnType)) {
            throw PersimmonExceptionFactory.create("method:" + method.getName() + " has" + annotationClass().getSimpleName() + ", return type must be void");
        }
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            // 检查参数是否实现了Serializable接口
            if (parameter.getType() != null && parameter.getType() instanceof Serializable) {
                // 参数实现了Serializable接口，继续检查
            } else {
                throw PersimmonExceptionFactory.create("method:" + method.getName() + " has" + annotationClass().getSimpleName() + ", parameter type must implements Serializable");
            }
        }
    }

    protected Listener<T, M> createListener(A annotation, Object bean, Method method) {
        return message -> {
            try {
                T data = message.getData();
                if (data instanceof Object[]) {
                    method.invoke(bean, (Object[]) data);
                } else {
                    method.invoke(bean, data);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

    @Around("executeService()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            MethodAopInfo methodAopInfo = new MethodAopInfo(proceedingJoinPoint);
            Method method = methodAopInfo.getMethod();
            A annotation = method.getAnnotation(this.annotationClass());
            M message = this.createMessage(annotation, methodAopInfo.getArgs());
            C context = this.createContext(annotation);
            this.publishMessage(message, context);
            return null;
        } catch (Exception e) {
            return proceedingJoinPoint.proceed();
        }
    }
}
